﻿using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace DirectXSim
{
    public class Car
    {
        private int _racePosition;
        private int _lapNumber;
        private int _speedMph;
        private int _speedKph;
        private float _scaling;
        private Mesh _carMesh;
        private Material[] _carMeshMaterials;
        private Texture[] _carMeshTextures;
        private float _carMeshRadius;
        private Vector3 _carMeshPosition;
        private float _carAngle;
        private float _carStartX;
        private float _carStartY;
        private float _carStartZ;
        private float _carGameSpeed;
        private TimeSpan _bestLaptime;

        private float _maxSpeed = 0.5f;

        public Car(float startx, float starty, float startz)
        {
            _carStartX = startx;
            _carStartY = starty;
            _carStartZ = startz;
            _carMeshPosition = new Vector3(startx, starty, startz);
            _racePosition = 1;
            _lapNumber = 1;
             _speedMph = 0;
             _speedKph = 0;
            _scaling = 0.08f;
            _carAngle = 0.0f;
            _carGameSpeed = 0.0f;
            _bestLaptime = new TimeSpan(0, 0, 0, 0, 0);
        }

        public void ResetPosition()
        {
            _carMeshPosition = new Vector3(_carStartX, _carStartY, _carStartZ);
        }

        public Vector3 Position
        {
            get { return _carMeshPosition; }
        }

        public float Radius
        {
            get { return _carMeshRadius; }
        }

        public int LapNumber
        {
            get { return _lapNumber; }
            set { _lapNumber = value; }
        }

        public TimeSpan BestLapTime
        {
            get { return _bestLaptime; }
            set { _bestLaptime = value; }
        }

        public int SpeedMph
        {
            get { return _speedMph; }
        }

        public int SpeedKph
        {
            get { return _speedKph; }
        }

        public float Scaling
        {
            get { return _scaling; }
        }
        public float GameSpeed
        {
            get { return _carGameSpeed; }
        }

        public int RacePosition
        {
            get { return _racePosition; }
            set { _racePosition = value; }
        }

        public float Angle
        {
            get { return _carAngle; }
            //set { _carAngle = value; }
        }

        public void HandleCollision(CollisionType collisionType, int trackWidth)
        {
            switch(collisionType)
            {
                case CollisionType.None:
                    break;
                case CollisionType.Barrier:
                    //Crashed into a barrier - slow down the car
                    if (_carGameSpeed > 0)
                    {
                        _carGameSpeed = _carGameSpeed / 2;
                    }
                    //Move the car back from the barrier
                    if (_carMeshPosition.X < 1)
                    {
                        _carMeshPosition.X = 1 + _carMeshRadius;
                    }
                    else
                    {
                        _carMeshPosition.X = trackWidth - 1 - _carMeshRadius;
                    }
                    //Turn car angle away from barrier
                    if (_carAngle < 0.0f)
                    {
                        _carAngle += 0.2f;
                    }
                    else
                    {
                        _carAngle -= 0.2f;
                    }
                    break;
                case CollisionType.EndTrack:
                    ResetPosition();
                    _carAngle = 0.0f;
                    break;
                case CollisionType.Opponent:
                    //Move car away from opponent
                    if (_carAngle < 0.0f)
                    {
                        _carAngle += 0.2f;
                        _carMeshPosition.X += 0.02f;
                    }
                    else
                    {
                        _carAngle -= 0.2f;
                        _carMeshPosition.X -= 0.02f;
                    }
                    //Slow car down
                    if (_carGameSpeed > 0)
                    {
                        _carGameSpeed = _carGameSpeed / 2;
                    }
                    break;
                case CollisionType.Obstacle:
                    //Bounce car back off obstacle
                    if (_carGameSpeed < 0)
                    {
                        _carMeshPosition.Y += _carMeshRadius;
                    }
                    else
                    {
                        _carMeshPosition.Y -= 1;
                    }
                    //Stop the car
                    _carGameSpeed = 0.0f;
                    break;
                default:
                    break;
            }
            _speedMph = (int)(_carGameSpeed * 200.0);
            _speedKph = (int)(_speedMph * 1.609344f);
        }

        internal void TurnRight()
        {
            _carAngle -= 0.05f;
            if (_carAngle < (Math.PI * 2))
            {
                _carAngle += (float)Math.PI * 2;
            }
        }

        internal void TurnLeft()
        {
            _carAngle += 0.05f;
            if (_carAngle > (Math.PI * 2))
            {
                _carAngle -= (float)Math.PI * 2;
            }
        }

        internal void Brake()
        {
            //Brake faster than reverse acceleration
            if (_carGameSpeed >= 0.0f)
            {
                _carGameSpeed = _carGameSpeed - 0.01f;
            }
            else
            {
                _carGameSpeed = _carGameSpeed - 0.005f;
            }
            //Max reverse speed
            if (_carGameSpeed < -0.2f) _carGameSpeed = -0.2f;
            _speedMph = (int)(_carGameSpeed * 200.0);
            _speedKph = (int)(_speedMph * 1.609344f);
        }

        internal void HandBrake()
        {
            //handbrake
            _carGameSpeed = _carGameSpeed - 0.02f;
            if (_carGameSpeed < 0.0f) _carGameSpeed = 0.0f;
            _speedMph = (int)(_carGameSpeed * 200.0);
            _speedKph = (int)(_speedMph * 1.609344f);
        }

        internal void Accelerate()
        {
            //Accelerate
            _carGameSpeed = _carGameSpeed + 0.01f;
            //Max speed
            if (_carGameSpeed > _maxSpeed) _carGameSpeed = _maxSpeed;
            _speedMph = (int)(_carGameSpeed * 200.0);
            _speedKph = (int)(_speedMph * 1.609344f);
        }

        public void UpdatePosition(bool userCar)
        {
            if (!userCar)
            {
                _carGameSpeed += 0.005f;
                if (_carGameSpeed > _maxSpeed) _carGameSpeed = _maxSpeed;

                Random randMovement = new Random();
                if (randMovement.Next(100) > 50)
                {
                    _carAngle += 0.02f * (float)randMovement.NextDouble();
                }
                else
                {
                    _carAngle -= 0.02f * (float)randMovement.NextDouble();
                }
            }

             _carMeshPosition.Y += (float)Math.Cos(_carAngle) * _carGameSpeed;
             _carMeshPosition.X += (float)Math.Sin(_carAngle) * (_carGameSpeed / 4);
 
        }


        internal void HandlePickUp(ItemType myCarItem)
        {
            switch (myCarItem)
            {
                case ItemType.Boost:
                    _carGameSpeed += 0.2f;
                    break;
                case ItemType.NoCollision:
                    break;
                case ItemType.Slippery:
                    _carAngle += (float)Math.PI / 2;
                    break;
                case ItemType.Stop:
                    _carGameSpeed = 0.0f;
                    break;
                default:
                    break;
            }
        }

        public void LoadMesh(string filename, ref Device dev)
        {
            ExtendedMaterial[] materialarray;
            _carMesh = Mesh.FromFile(filename, MeshFlags.Managed, dev, out materialarray);
            if ((materialarray != null) && (materialarray.Length > 0))
            {
                _carMeshMaterials = new Material[materialarray.Length];
                _carMeshTextures = new Texture[materialarray.Length];

                for (int i = 0; i < materialarray.Length; i++)
                {
                    _carMeshMaterials[i] = materialarray[i].Material3D;
                    _carMeshMaterials[i].Ambient = _carMeshMaterials[i].Diffuse;

                    if ((materialarray[i].TextureFilename != null) && (materialarray[i].TextureFilename != string.Empty))
                    {
                        _carMeshTextures[i] = TextureLoader.FromFile(dev, materialarray[i].TextureFilename);
                    }
                }
            }
            _carMesh = _carMesh.Clone(_carMesh.Options.Value, CustomVertex.PositionNormalTextured.Format, dev);
            _carMesh.ComputeNormals();

            VertexBuffer vertices = _carMesh.VertexBuffer;
            GraphicsStream stream = vertices.Lock(0, 0, LockFlags.None);
            Vector3 meshcenter;
            _carMeshRadius = Geometry.ComputeBoundingSphere(stream, _carMesh.NumberVertices, _carMesh.VertexFormat, out meshcenter) * _scaling;
            vertices.Unlock();
        }

        internal void Draw(ref Device dev)
        {
            for (int i = 0; i < _carMeshMaterials.Length; i++)
            {
                dev.Material = _carMeshMaterials[i];
                dev.SetTexture(0, _carMeshTextures[i]);
                _carMesh.DrawSubset(i);
            }
        }
    }
}
